/**
	The Minerva Project, DAG Scheduling Tools
	Copyright (C) 2006 Florin Pop & Stefan Bucur

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
 */
package ro.pub.cs.minerva.scheduler;

import java.util.Collection;
import java.util.HashMap;

import javax.swing.event.EventListenerList;

import ro.pub.cs.minerva.graph.GraphException;
import ro.pub.cs.minerva.resource.Resource;
import ro.pub.cs.minerva.task.TaskDepsGraph;

/**
 * @author Stefan Bucur
 *
 */
public abstract class TaskScheduler {
	private EventListenerList scheduleListenerList =
		new EventListenerList();
	
	/**
	 * The tasks dependency graph
	 */
	protected TaskDepsGraph taskDepsGraph = new TaskDepsGraph();
	
	/**
	 * The available resources
	 */
	protected HashMap<String, Resource> resources = new HashMap<String, Resource>();
	
	/**
	 *
	 */
	protected TaskScheduleResult scheduleResult = new TaskScheduleResult();
	
	public TaskScheduler() {
		
	}
	
	public abstract void scheduleNextTask();
	
	public abstract void scheduleAllTasks();
	
	public void addTaskDepsInfo(TaskDepsGraph newDepsInfo) {
		if (newDepsInfo == null)
			throw new NullPointerException("The added information is null");
		
		try {
			taskDepsGraph.mergeGraph(newDepsInfo);
		} catch (GraphException ex) {
			throw new SchedulerException("The new info cannot be merged", ex);
		}
		
		if (newDepsInfo.isCyclic())
			throw new SchedulerException("The resulting graph is cyclical");
	}
	
	
	public void addScheduleListener(ScheduleListener listener) {
		scheduleListenerList.add(ScheduleListener.class, listener);
	}
	
	public void removeScheduleListener(ScheduleListener listener) {
		scheduleListenerList.remove(ScheduleListener.class, listener);
	}
	
	public ScheduleListener[] getScheduleListeners() {
		return scheduleListenerList.getListeners(ScheduleListener.class);
	}
	
	public TaskDepsGraph getCurrentDepsGraph() {
		return taskDepsGraph;
	}
	
	/**
	 * 
	 * @param resource
	 */
	public void addResource(Resource resource) {
		if (this.resources.containsValue(resource))
			return;
		
		this.resources.put(resource.getProcId(), resource);
		
		if (!scheduleResult.containsResource(resource))
			scheduleResult.addResource(resource);
	}
	
	public void addAllResources(Collection<? extends Resource> resources) {
		for (Resource resource : resources) {
			addResource(resource);
		}
	}
	
	/**
	 * Removes a resource from the task scheduler algorithm. That is, this
	 * method instructs the algorithm not to take anymore into consideration the
	 * resource in the scheduling process of the rest of the tasks. However, any tasks
	 * already scheduled on that resource will not change their status.
	 * @param resource
	 */
	public void removeResource(Resource resource) {
		if (!this.resources.containsValue(resource))
			return;
		
		if (scheduleResult.getMappingCount(resource) == 0) {
			scheduleResult.removeResource(resource);
		}
		
		resources.remove(resource.getProcId());
	}
	
	public Resource[] getResources() {
		return resources.values().toArray(new Resource[0]);
	}
	
	public Resource findResource(String procID) {
		return resources.get(procID);
	}
	
	public TaskScheduleResult getScheduleResult() {
		return scheduleResult;
	}
}
